Encapsulate pipette batch scheduling into backend-agnostic module#949
Encapsulate pipette batch scheduling into backend-agnostic module#949BioCam wants to merge 17 commits intoPyLabRobot:mainfrom
Conversation
Replace hamilton/planning.py with pipette_batch_scheduling.py, a self-contained module for channel-batch planning, Y-position computation, and X-group scheduling. Refactor STAR_backend's probe_liquid_heights and execute_batched to use the new API. Add volume-tracker-based probe_liquid_heights mock to chatterbox. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Pull request overview
This PR extracts pipette channel batching/scheduling logic from the Hamilton STARBackend into a new backend-agnostic module, and rewrites probe_liquid_heights to use the new planner while adding tests for the scheduling algorithm.
Changes:
- Added
pipette_batch_scheduling.pywithplan_batches()(X grouping + Y sub-batching), phantom-channel interpolation, pairwise span validation, and optional batch-transition lookahead optimization. - Rewrote
STARBackend.probe_liquid_heights()to useplan_batches()and the new helper utilities (input validation, offset computation, absolute position computation). - Replaced the old Hamilton-only
planning.pyand its tests with new dedicated unit tests for the new scheduling module.
Reviewed changes
Copilot reviewed 7 out of 7 changed files in this pull request and generated 5 comments.
Show a summary per file
| File | Description |
|---|---|
pylabrobot/liquid_handling/pipette_batch_scheduling.py |
New backend-agnostic batching/scheduling module (plan_batches, transition optimization, helpers). |
pylabrobot/liquid_handling/pipette_batch_scheduling_tests.py |
New unit tests covering spacing logic, phantom interpolation, batching, and transition optimization. |
pylabrobot/liquid_handling/backends/hamilton/STAR_backend.py |
Integrates new planner into probe_liquid_heights, adjusts spacing-related logic, removes legacy batching helpers. |
pylabrobot/liquid_handling/backends/hamilton/STAR_chatterbox.py |
Adds missing mock methods and a simulation-friendly probe_liquid_heights implementation using shared validation. |
pylabrobot/liquid_handling/backends/hamilton/planning.py |
Removed legacy Hamilton-only batching module. |
pylabrobot/liquid_handling/backends/hamilton/planning_tests.py |
Removed tests for the deleted legacy planner. |
pylabrobot/liquid_handling/backends/hamilton/STAR_tests.py |
Refactors tests to exercise the new probe_liquid_heights flow (and rehomes some test classes). |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| if spread == "wide": | ||
| offsets = get_wide_single_resource_liquid_op_offsets( | ||
| resource=well, | ||
| num_channels=len(piercing_channels), | ||
| min_spacing=self._get_maximum_minimum_spacing_between_channels(piercing_channels), | ||
| min_spacing=max(self._channels_minimum_y_spacing), | ||
| ) |
There was a problem hiding this comment.
In pierce_foil(spread='wide'), min_spacing is now max(self._channels_minimum_y_spacing) (global max across the whole head). This can overspread channels unnecessarily (or even beyond the well/plate geometry) when the operation uses only a subset of channels with smaller pairwise constraints. Restore the previous behavior by computing spacing based on the piercing_channels subset (e.g., maximum required adjacent-pair spacing across the involved physical span).
There was a problem hiding this comment.
with the removal of execute_batched, the code becomes a lot less modular. and we do want to use execute_batched for other commands in the future
There was a problem hiding this comment.
interesting, the current execute_batched seemed very limited to probing actions, which is why I removed it, but you're saying that it actually acts as an abstraction layer for any function that is meant to be called after the channels have moved to their target locations - I really like this
I will work on an implementation
There was a problem hiding this comment.
with small adjustment to this workflow we can use it for...
- probing (ztouch, cLLD, pLLD)
- aspirate
- dispense
…odd-span wall crash - plan_batches now takes targets (Containers or Coordinates) and handles position computation and same-container spreading internally, replacing the external compute_offsets + compute_positions + plan_batches sequence - Restore execute_batched on STARBackend; probe_liquid_heights uses it via _probe_batch_heights closure instead of an inline batch loop - Make +5.5mm odd-span center-avoidance offset conditional on container width to prevent tip-wall collisions on narrow containers - Generalize compute_positions to accept any Resource (wrt_resource), not just Deck - Remove dead code: _optimize_batch_transitions (LATER :), _find_next_y_target - Rename validate_probing_inputs -> validate_channel_selections - Clean up redundant tests, add container-path coverage
There was a problem hiding this comment.
why does CB need probe_liquid_heights? ideally it should share the same logic as the star backend equivalent. previously I just overrode the measurement bit because that part we can't know in CB
There was a problem hiding this comment.
because....
- Chatterbox code needs to work completely interchangeably with its backend/driver counterpart without the need to say
if protocol_mode == "simulation"every time. - I disagree with...
previously I just overrode the measurement bit because that part we can't know in CB
...the simulation/chatterbox actually has a unique opportunity here:
I made it return what the liquid tracker returns - this enables true simulation :)

Extracts batch scheduling logic from
STARBackendintopipette_batch_scheduling.py- a standalone, backend-agnostic module with full test coverage.The Problem
probe_liquid_heightsrelied on several tightly coupled private methods (execute_batched,_probe_liquid_heights_batch,_compute_channels_in_resource_locations,_move_to_traverse_height) embedded inSTARBackend. The scheduling algorithm (X grouping, Y sub-batching) was inseparable from Hamilton-specific hardware calls, making it untestable in isolation and not easily reusable by other backends.While per-channel spacing support was already merged (PRs #862, #870, #915), the scheduling layer had gaps: non-consecutive channel batches (e.g. [0,1,2,5,6,7]) left intermediate physical channels 3,4 unpositioned, Y batch feasibility used a single-pair check rather than full pairwise span validation, and there was no lookahead between batches.
PR Content/Solution
New module:
pipette_batch_scheduling.pyplan_batches()partitions channel-position pairs into executable batches. Backend-agnostic - depends only on channel indices, positions, and spacing constraints.New capabilities
_span_required(sum of adjacent pairwise spacings), not just the gap between the candidate and the batch's lowest-Y member. This matters for mixed-channel instruments wherespacing(ch0->ch3)iss(0,1) + s(1,2) + s(2,3), not3 * max(spacings).round(x, 1)(Python's banker's rounding) withmath.floor(x / tolerance) * toleranceand exposesx_grouping_toleranceas a parameter.Structural changes
probe_liquid_heightsrewritten - replaces the 5-method delegation chain (execute_batchedwith callback closure ->_probe_liquid_heights_batch->_compute_channels_in_resource_locations->_move_to_traverse_height) with a single linear flow callingplan_batches(). The method is longer (262 vs 124 lines) but reads top-to-bottom without jumping between methods or files.planning.py- the old module provided X grouping and Y sub-batching but lacked phantom interpolation, span validation, and transition lookahead. Deleted along with its tests.num_channels(not justmax(use_channels)+1), preventingIndexErrorin transition optimization.Not in scope
Detection parameter exposure (cLLD/pLLD kwargs) is intentionally deferred to a follow-up PR to keep this change focused on scheduling encapsulation.
Preview
a small taste of the new functionalities (GitHub doesn't allow larger videos)
WellPlateScene_with_logo.mp4